/******************************************************************
*
*	CyberHTTP for Java
*
*	Copyright (C) Satoshi Konno 2002-2003
*
*	File: HTTPServer.java
*
*	Revision;
*
*	12/12/02
*		- first revision.
*	10/20/03
*		- Improved the HTTP server using multithreading.
*	
******************************************************************/

package com.cidero.http;

import java.io.*;
import java.net.*;
import java.util.logging.Logger;

import org.cybergarage.util.*;

public class HTTPServer implements Runnable
{
  private static Logger logger = Logger.getLogger("com.cidero.http");

	////////////////////////////////////////////////
	//	Constants
	////////////////////////////////////////////////

	public final static String NAME = "CyberHTTP";
	public final static String VERSION = "1.0";

	public final static int DEFAULT_PORT = 80;

	public static String getName()
	{
		String osName = System.getProperty("os.name");
		String osVer = System.getProperty("os.version");
		return osName + "/"  + osVer + " " + NAME + "/" + VERSION;
	}
	
	////////////////////////////////////////////////
	//	Constructor
	////////////////////////////////////////////////
	
	public HTTPServer()
	{
		serverSock = null;
	}

	////////////////////////////////////////////////
	//	ServerSocket
	////////////////////////////////////////////////

	private ServerSocket serverSock = null;
	private InetAddress bindAddr = null;
	private int bindPort = 0;
	
	public ServerSocket getServerSock()
	{
		return serverSock;
	}

	public String getBindAddress()
	{
			if (bindAddr == null)
				return "";
			return bindAddr.toString();
	}

	public int getBindPort()
	{
		return bindPort;
	}
	
	////////////////////////////////////////////////
	//	open/close
	////////////////////////////////////////////////
	
	public boolean open(String addr, int port)
	{
		if (serverSock != null)
			return true;
		try {
			bindAddr = InetAddress.getByName(addr);
			bindPort = port;
			serverSock = new ServerSocket(bindPort, 0, bindAddr);
		}
		catch (IOException e) {
			return false;
		}
		return true;
	}

	public void close()
	{
		if (serverSock == null)
			return;
		try {
			serverSock.close();
			serverSock = null;
			bindAddr = null;
			bindPort = 0;
		}
		catch (Exception e) {
			Debug.warning(e);
		}
	}

	public Socket accept()
	{
		if (serverSock == null)
			return null;

		try {
			return serverSock.accept();
		}
		catch (Exception e) {
			return null;
		}
	}

	public boolean isOpened()
	{
		return (serverSock != null) ? true : false;
	}

	////////////////////////////////////////////////
	//	httpRequest
	////////////////////////////////////////////////

	private ListenerList httpRequestListenerList = new ListenerList();
	 	
	public void addRequestListener( HTTPRequestListener listener )
	{
		httpRequestListenerList.add(listener);
	}		

	public void removeRequestListener( HTTPRequestListener listener )
	{
		httpRequestListenerList.remove(listener);
	}		

  /**
   *  Invoke request listener callbacks. If one of the callbacks takes over
   *  the HTTP session, it returns false - pass on the flag to the server
   *  thread
   */
	public boolean performRequestListener( HTTPRequest httpReq )
	{
    boolean shutdownFlag = true;
    
		int listenerSize = httpRequestListenerList.size();
		for (int n=0; n<listenerSize; n++)
    {
			HTTPRequestListener listener = (HTTPRequestListener)httpRequestListenerList.get(n);

			if( listener.httpRequestReceived(httpReq) == false )
        shutdownFlag = false;
		}

    return shutdownFlag;
	}		
	
	////////////////////////////////////////////////
	//	run	
	////////////////////////////////////////////////

	private Thread httpServerThread = null;
		
	public void run()
	{
		if (isOpened() == false)
			return;
			
		Thread thisThread = Thread.currentThread();
		
		while (httpServerThread == thisThread)
    {
			Thread.yield();
			Socket sock;

			try {
        // TODO: accept method in this class eats exceptions - FIX
				sock = accept();
        if( sock == null )
          break;

        //logger.info("HTTPServer: creating new thread");
        HTTPServerThread httpServThread = new HTTPServerThread(this, sock);
        httpServThread.start(); 
			}
			catch (Exception e){
				logger.warning( "accept error" );
        break;
			}
		}
    //logger.info("HTTPServer: Exiting run method");
	}
	
	public void start()
	{
		httpServerThread = new Thread(this);
		httpServerThread.start();
	}
	
	public void stop()
	{
		httpServerThread = null;
	}
}
